
#include "nane/RenderSystem/d3d9/D3D9RenderSystem.h"

#include "nane/RenderSystem/d3d9/D3D9ConstantBuffer.h"
#include "nane/RenderSystem/d3d9/D3D9VertexBuffer.h"
#include "nane/RenderSystem/d3d9/D3D9IndexBuffer.h"
#include "nane/RenderSystem/d3d9/D3D9Texture.h"

#include "nane/core/assert.h"
#include "nane/core/log.h"

#define MAX_VERTEX_ELEMENTS 20

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    extern "C"
    {
        typedef IDirect3D9* (WINAPI *PDIRECT3DCREATE9)(UINT);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static const D3DFORMAT D32SFormats[] = { D3DFMT_D24S8, D3DFMT_D24X4S4, D3DFMT_D15S1, D3DFMT_D32, D3DFMT_D24X8, D3DFMT_D16, (D3DFORMAT) 0 };
    static const D3DFORMAT D32Formats[] = { D3DFMT_D32, D3DFMT_D24X8, D3DFMT_D24S8, D3DFMT_D24X4S4, D3DFMT_D16, D3DFMT_D15S1, (D3DFORMAT) 0 };
    static const D3DFORMAT D16SFormats[] = { D3DFMT_D15S1, D3DFMT_D24S8, D3DFMT_D24X4S4, D3DFMT_D16, D3DFMT_D32, D3DFMT_D24X8, (D3DFORMAT) 0 };
    static const D3DFORMAT D16Formats[] = { D3DFMT_D16, D3DFMT_D15S1, D3DFMT_D32, D3DFMT_D24X8, D3DFMT_D24S8, D3DFMT_D24X4S4, (D3DFORMAT) 0 };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static int s_format_id_( D3DFORMAT _format )
    {
        switch(_format)
        {
            case D3DFMT_R5G6B5: return 1;
            case D3DFMT_X1R5G5B5: return 2;
            case D3DFMT_A1R5G5B5: return 3;
            case D3DFMT_X8R8G8B8: return 4;
            case D3DFMT_A8R8G8B8: return 5;
            default:;
        }
        return 0;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static D3DPRIMITIVETYPE s_toD3DPrimitiveType[EPrimitiveType::size] =
    {
        D3DPT_POINTLIST,
        D3DPT_LINELIST,
        D3DPT_LINESTRIP,
        D3DPT_TRIANGLELIST,
        D3DPT_TRIANGLESTRIP,
        D3DPT_TRIANGLEFAN
    };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static BYTE s_toD3DVertDeclType[] =
    {
        D3DDECLTYPE_FLOAT1,
        D3DDECLTYPE_FLOAT2,
        D3DDECLTYPE_FLOAT3,
        D3DDECLTYPE_FLOAT4,
        D3DDECLTYPE_D3DCOLOR,
        D3DDECLTYPE_UBYTE4,
        D3DDECLTYPE_SHORT2,
        D3DDECLTYPE_SHORT4
    };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static BYTE s_toD3DVertDeclUsage[] =
    {
        D3DDECLUSAGE_POSITION,
        D3DDECLUSAGE_BLENDWEIGHT,
        D3DDECLUSAGE_BLENDINDICES,
        D3DDECLUSAGE_NORMAL,
        D3DDECLUSAGE_PSIZE,
        D3DDECLUSAGE_TEXCOORD,
        D3DDECLUSAGE_TANGENT,
        D3DDECLUSAGE_BINORMAL,
        D3DDECLUSAGE_TESSFACTOR,
        D3DDECLUSAGE_COLOR,
        D3DDECLUSAGE_FOG,
        D3DDECLUSAGE_DEPTH,
        D3DDECLUSAGE_SAMPLE

    };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static D3DCULL s_toD3DCullMode[ECullMode::size] =
    {
        D3DCULL_NONE,
        D3DCULL_CW,
        D3DCULL_CCW
    };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static D3DFORMAT s_toD3DFormat[EPixelFormat::size] =
    {
        D3DFMT_UNKNOWN,         // Unknown
        D3DFMT_A8R8G8B8,        // A8R8G8B8
    };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    D3DFORMAT D3D9RenderSystem::s_findMatchingZFormat_( IDirect3D9* _pD3D, UINT _adapter, D3DDEVTYPE _devtype, D3DFORMAT _bbufferfmt, bool _stencil )
    {
        NANE_ASSERT( _pD3D != NULL, "" );
        int fmtID = s_format_id_( _bbufferfmt );
        if( fmtID == 0 )
        {
            return static_cast<D3DFORMAT>( 0 );
        }
        const D3DFORMAT *pFormatList;
        if( fmtID > 3 ) // 32bit
        {
            pFormatList = _stencil ? D32SFormats : D32Formats;
        }
        else
        {
            pFormatList = _stencil ? D16SFormats : D16Formats;
        }

        while( *pFormatList != 0 )
        {
            // Does this depth format exist on this card, and can it be used in conjunction with the specified rendertarget format?
            if( SUCCEEDED(_pD3D->CheckDeviceFormat(_adapter, _devtype, _bbufferfmt, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, *pFormatList)) )
            {
                if( SUCCEEDED(_pD3D->CheckDepthStencilMatch(_adapter, _devtype, _bbufferfmt, _bbufferfmt, *pFormatList)) )
                {
                    break;
                }
            }
            ++pFormatList;
        }

        return *pFormatList;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    D3D9RenderSystem::D3D9RenderSystem()
        : m_pD3D( NULL )
        , m_hDLL( NULL )
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    D3D9RenderSystem::~D3D9RenderSystem()
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::Initialize()
    {
        m_hDLL = ::LoadLibraryA("d3d9.dll");
        if( m_hDLL == NULL )
        {
            LOG_ERROR("Failed to load d3d9.dll");
            return false;
        }

        PDIRECT3DCREATE9 pDirect3DCreate9 = reinterpret_cast<PDIRECT3DCREATE9>(::GetProcAddress(m_hDLL, "Direct3DCreate9"));
        if( pDirect3DCreate9 == NULL )
        {
            LOG_ERROR("Failed to get Direct3DCreate9 function address");
            Shutdown();
            return false;
        }

        m_pD3D = pDirect3DCreate9(D3D_SDK_VERSION);
        if( m_pD3D == NULL )
        {
            LOG_ERROR("Failed to create D3D instance");
            Shutdown();
            return false;
        }

        if( FAILED(m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &m_systemDisplayMode)) || m_systemDisplayMode.Format == D3DFMT_UNKNOWN )
        {
            LOG_ERROR( "Can't determine desktop video mode" );
            Shutdown();
            return false;
        }

        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::Shutdown()
    {
        if( m_pDevice != NULL )
        {
            m_pDevice->Release();
            m_pDevice = NULL;
        }

        if( m_pD3D != NULL )
        {
            m_pD3D->Release();
            m_pD3D = NULL;
        }
        
        if( m_hDLL != NULL )
        {
            ::FreeLibrary(m_hDLL);
            m_hDLL = NULL;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::InitializeContext( IWindow::OSHandle _windowHandle, uint32 _resWidth, uint32 _resHeight )
    {
        HWND hWnd = static_cast<HWND>(_windowHandle);
        m_presentParams.BackBufferWidth = _resWidth;
        m_presentParams.BackBufferHeight = _resHeight;
        m_presentParams.BackBufferFormat = m_systemDisplayMode.Format;
        m_presentParams.BackBufferCount = 1;
        m_presentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
        m_presentParams.MultiSampleQuality = 0;
        m_presentParams.SwapEffect = D3DSWAPEFFECT_COPY;
        m_presentParams.hDeviceWindow = hWnd;
        m_presentParams.Windowed = TRUE;
        m_presentParams.EnableAutoDepthStencil = TRUE;
        m_presentParams.AutoDepthStencilFormat = s_findMatchingZFormat_(m_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_presentParams.BackBufferFormat, false);
        m_presentParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
        m_presentParams.FullScreen_RefreshRateInHz = 0;
        m_presentParams.PresentationInterval  = D3DPRESENT_INTERVAL_IMMEDIATE;
        
        m_activeVertexConstantBuffer = NULL;
        m_activeVertexConstantBufferSlot = 0;
        m_activePixelConstantBuffer = NULL;
        m_activePixelConstantBufferSlot = 0;
        
        HRESULT hr = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE , &m_presentParams, &m_pDevice);
        return SUCCEEDED(hr);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IVertexBuffer* D3D9RenderSystem::CreateVertexBuffer( EResourceUsage::raw_type _usage, uint32 _verticesNum, uint32 _vertexSize
                                                       , VertexElement* _elements, uint32 _elementsNum, byte* _initData )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3D9VertexBuffer* d3d9VertexBuffer = NULL;
        DWORD usage = _usage == EResourceUsage::Writeonly ? D3DUSAGE_WRITEONLY : 0;
        D3DPOOL pool = D3DPOOL_MANAGED;         // maybe need default
        IDirect3DVertexBuffer9* pVB = NULL;
        HRESULT hr = m_pDevice->CreateVertexBuffer(_verticesNum*_vertexSize, usage, 0, pool, &pVB, NULL);
        IDirect3DVertexDeclaration9* pVertexDeclaration = createVertexDeclaration_(_elements, _elementsNum);
        if( SUCCEEDED(hr) && pVB != NULL && pVertexDeclaration != NULL )
        {
            d3d9VertexBuffer = new D3D9VertexBuffer(pVB, _vertexSize, pVertexDeclaration);
            byte* bufferLock = LockVertexBuffer(d3d9VertexBuffer, 0, _verticesNum*_vertexSize);
            if( bufferLock != NULL )
            {
                memcpy(bufferLock, _initData, _verticesNum*_vertexSize);
                UnlockVertexBuffer(d3d9VertexBuffer);
            }
        }
        return d3d9VertexBuffer;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ReleaseVertexBuffer( IVertexBuffer* _vertexBuffer )
    {
        D3D9VertexBuffer* d3d9VertexBuffer = static_cast<D3D9VertexBuffer*>(_vertexBuffer);
        if( d3d9VertexBuffer != NULL )
        {
            d3d9VertexBuffer->pVertexDeclaration->Release();
            d3d9VertexBuffer->pVB9->Release();
            delete d3d9VertexBuffer;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    byte* D3D9RenderSystem::LockVertexBuffer( IVertexBuffer* _vertexBuffer, uint32 _offset, uint32 _size )
    {
        byte* lockedMemory = NULL;
        D3D9VertexBuffer* d3d9VertexBuffer = static_cast<D3D9VertexBuffer*>(_vertexBuffer);
        if( d3d9VertexBuffer != NULL )
        {
            HRESULT hr = d3d9VertexBuffer->pVB9->Lock(_offset, _size, reinterpret_cast<void**>(&lockedMemory), 0);
            SUCCEEDED(hr);
        }
        return lockedMemory;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::UnlockVertexBuffer( IVertexBuffer* _vertexBuffer )
    {
        D3D9VertexBuffer* d3d9VertexBuffer = static_cast<D3D9VertexBuffer*>(_vertexBuffer);
        if( d3d9VertexBuffer != NULL )
        {
            HRESULT hr = d3d9VertexBuffer->pVB9->Unlock();
            SUCCEEDED(hr);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetVertexBuffer( uint16 _streamNumber, IVertexBuffer* _vertexBuffer )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3D9VertexBuffer* d3d9VertexBuffer = static_cast<D3D9VertexBuffer*>(_vertexBuffer);
        if( d3d9VertexBuffer != NULL )
        {
            HRESULT hr = m_pDevice->SetStreamSource(_streamNumber, d3d9VertexBuffer->pVB9, 0, d3d9VertexBuffer->elementSize);
            SUCCEEDED(hr);
            hr = m_pDevice->SetVertexDeclaration(d3d9VertexBuffer->pVertexDeclaration);
            SUCCEEDED(hr);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IIndexBuffer* D3D9RenderSystem::CreateIndexBuffer( EResourceUsage::raw_type _usage, uint32 _indiciesNum, uint16* _initData )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3D9IndexBuffer* d3d9IndexBuffer = NULL;
        DWORD usage = _usage == EResourceUsage::Writeonly ? D3DUSAGE_WRITEONLY : 0;
        D3DPOOL pool = D3DPOOL_MANAGED;
        IDirect3DIndexBuffer9* pIB = NULL;
        HRESULT hr = m_pDevice->CreateIndexBuffer(_indiciesNum*sizeof(uint16), usage, D3DFMT_INDEX16, pool, &pIB, NULL);
        if( SUCCEEDED(hr) && pIB != NULL )
        {
            d3d9IndexBuffer = new D3D9IndexBuffer(pIB, _indiciesNum);
            uint16* bufferLock = d3d9IndexBuffer->Lock(0, _indiciesNum*sizeof(uint16));
            if( bufferLock != NULL )
            {
                memcpy(bufferLock, _initData, _indiciesNum*sizeof(uint16));
                d3d9IndexBuffer->Unlock();
            }
        }
        return d3d9IndexBuffer;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ReleaseIndexBuffer( IIndexBuffer* _indexBuffer )
    {
        D3D9IndexBuffer* d3d9IndexBuffer = static_cast<D3D9IndexBuffer*>(_indexBuffer);
        if( d3d9IndexBuffer != NULL )
        {
            IDirect3DIndexBuffer9* pIB = d3d9IndexBuffer->GetInterface();
            delete d3d9IndexBuffer;
            pIB->Release();
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint16* D3D9RenderSystem::LockIndexBuffer( IIndexBuffer* _indexBuffer, uint32 _offset, uint32 _size )
    {
        uint16* lockedMemory = NULL;
        D3D9IndexBuffer* d3d9IndexBuffer = static_cast<D3D9IndexBuffer*>(_indexBuffer);
        if( d3d9IndexBuffer != NULL )
        {
            lockedMemory = d3d9IndexBuffer->Lock(_offset, _size);
        }
        return lockedMemory;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::UnlockIndexBuffer( IIndexBuffer* _indexBuffer )
    {
        D3D9IndexBuffer* d3d9IndexBuffer = static_cast<D3D9IndexBuffer*>(_indexBuffer);
        if( d3d9IndexBuffer != NULL )
        {
            d3d9IndexBuffer->Unlock();
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetIndexBuffer( IIndexBuffer* _indexBuffer )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3D9IndexBuffer* d3d9IndexBuffer = static_cast<D3D9IndexBuffer*>(_indexBuffer);
        if( d3d9IndexBuffer != NULL )
        {
            IDirect3DIndexBuffer9* pIB = d3d9IndexBuffer->GetInterface();
            HRESULT hr = m_pDevice->SetIndices(pIB);
            SUCCEEDED(hr);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ITexture* D3D9RenderSystem::CreateTexture( EResourceUsage::raw_type _usage, uint32 _width, uint32 _height, EPixelFormat::raw_type _format )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3D9Texture* d3d9Texture = NULL;
        IDirect3DTexture9* pTexture = NULL;
        D3DPOOL pool = D3DPOOL_MANAGED;
        HRESULT hr = m_pDevice->CreateTexture(_width, _height, 1, 0, s_toD3DFormat[_format], pool, &pTexture, NULL);
        if( SUCCEEDED(hr) )
        {
            d3d9Texture = new D3D9Texture(pTexture);
        }
        return d3d9Texture;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ReleaseTexture( ITexture* _texture )
    {
        D3D9Texture* d3d9Texture = static_cast<D3D9Texture*>(_texture);
        if( d3d9Texture != NULL )
        {
            IDirect3DTexture9* pTexture = d3d9Texture->getInterface();
            delete d3d9Texture;
            pTexture->Release();
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    byte* D3D9RenderSystem::LockTexture( ITexture* _texture, uint32 _x, uint32 _y, uint32 _width, uint32 _height, uint32* _pitch )
    {
        byte* lockedMemory = NULL;
        D3D9Texture* d3d9Texture = static_cast<D3D9Texture*>(_texture);
        if( d3d9Texture != NULL )
        {
            lockedMemory = d3d9Texture->Lock(_x, _y, _width, _height, _pitch);
        }
        return lockedMemory;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::UnlockTexture( ITexture* _texture )
    {
        D3D9Texture* d3d9Texture = static_cast<D3D9Texture*>(_texture);
        if( d3d9Texture != NULL )
        {
            d3d9Texture->Unlock();
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetTexture( uint32 _stage, ITexture* _texture )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3D9Texture* d3d9Texture = static_cast<D3D9Texture*>(_texture);
        if( d3d9Texture != NULL )
        {
            IDirect3DTexture9* pTexture = d3d9Texture->getInterface();
            HRESULT hr = m_pDevice->SetTexture(_stage, pTexture);
            SUCCEEDED(hr);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::SetRenderTarget( IRenderTarget* _target )
    {
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::BeginScene()
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        HRESULT hr = m_pDevice->BeginScene();
        return SUCCEEDED(hr);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::EndScene()
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        HRESULT hr = m_pDevice->EndScene();
        SUCCEEDED(hr);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::DrawPrimitive( EPrimitiveType::raw_type _type, uint32 _startVertex, uint32 _primitiveCount )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        HRESULT hr = m_pDevice->DrawPrimitive( s_toD3DPrimitiveType[_type], _startVertex, _primitiveCount );
        SUCCEEDED(hr);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::DrawIndexedPrimitive( EPrimitiveType::raw_type _type, uint32 _baseVertexIndex, uint32 _minIndex
                                     , uint32 _verticesNum, uint32 _startIndex, uint32 _primitiveCount )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        HRESULT hr = m_pDevice->DrawIndexedPrimitive( s_toD3DPrimitiveType[_type], _baseVertexIndex, _minIndex
                                                    , _verticesNum, _startIndex, _primitiveCount );
        SUCCEEDED(hr);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ClearRenderTarget( EFrameBufferType::raw_type _type, uint32 _color, float _depth, uint16 _stencil )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        DWORD frameBufferFlags = 0;
        if( (_type & EFrameBufferType::Color) != 0 )
        {
            frameBufferFlags |= D3DCLEAR_TARGET;
        }
        if( (_type & EFrameBufferType::Depth) != 0 )
        {
            frameBufferFlags |= D3DCLEAR_ZBUFFER;
        }
        if( (_type & EFrameBufferType::Stencil) != 0 )
        {
            frameBufferFlags |= D3DCLEAR_STENCIL;
        }
        HRESULT hr = m_pDevice->Clear(0, NULL, frameBufferFlags, _color, _depth, _stencil);
        if( FAILED( hr ) )
        {
            LOG_ERROR("Error: failed to clearFrameBuffer (hr:%1)") << hr;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SwapBuffers()
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        HRESULT hr = m_pDevice->Present(NULL, NULL, NULL, NULL);
        if( FAILED( hr ) )
        {
            LOG_ERROR("Error: Failed to swap buffers");
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetProjectionMatrix( const float* _projection )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        m_pDevice->SetTransform(D3DTS_PROJECTION, (D3DMATRIX*)_projection);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetModelViewMatrix( const float* _modelview )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        m_pDevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)_modelview);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetLightingEnable( bool _enable )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        m_pDevice->SetRenderState(D3DRS_LIGHTING, _enable ? TRUE : FALSE);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::SetCullMode( ECullMode::raw_type _mode )
    {
        m_pDevice->SetRenderState(D3DRS_CULLMODE, s_toD3DCullMode[_mode]);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IConstantBuffer* D3D9RenderSystem::CreateConstantBuffer( uint32 _size )
    {
        return new D3D9ConstantBuffer(_size);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::PushConstantF( IConstantBuffer* _constantBuffer, uint32 _offset, float* _constant, uint32 _size )
    {
        D3D9ConstantBuffer* d3d9ConstantBuffer = static_cast<D3D9ConstantBuffer*>(_constantBuffer);
        if( _offset + _size > d3d9ConstantBuffer->size )
        {
            return false;
        }
        
        if( d3d9ConstantBuffer->floatBuffer.empty() != false )
        {
            d3d9ConstantBuffer->floatBuffer.resize(d3d9ConstantBuffer->size);
        }
        
        byte* data = reinterpret_cast<byte*>(_constant);
        std::copy(data, data + _size, d3d9ConstantBuffer->floatBuffer.begin() + _offset);
        
        if( m_activeVertexConstantBuffer == d3d9ConstantBuffer )
        {
            SetVertexShaderConstantBuffer(m_activeVertexConstantBufferSlot, m_activeVertexConstantBuffer);
        }
        if( m_activePixelConstantBuffer == d3d9ConstantBuffer )
        {
            SetPixelShaderConstantBuffer(m_activePixelConstantBufferSlot, m_activePixelConstantBuffer);
        }
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::PushConstantI( IConstantBuffer* _constantBuffer, uint32 _offset, int* _constant, uint32 _size )
    {
        D3D9ConstantBuffer* d3d9ConstantBuffer = static_cast<D3D9ConstantBuffer*>(_constantBuffer);
        if( _offset + _size > d3d9ConstantBuffer->size )
        {
            return false;
        }
        
        if( d3d9ConstantBuffer->intBuffer.empty() != false )
        {
            d3d9ConstantBuffer->intBuffer.resize(d3d9ConstantBuffer->size);
        }        
        
        byte* data = reinterpret_cast<byte*>(_constant);
        std::copy(data, data + _size, d3d9ConstantBuffer->intBuffer.begin() + _offset);
        
        if( m_activeVertexConstantBuffer == d3d9ConstantBuffer )
        {
            SetVertexShaderConstantBuffer(m_activeVertexConstantBufferSlot, m_activeVertexConstantBuffer);
        }
        if( m_activePixelConstantBuffer == d3d9ConstantBuffer )
        {
            SetPixelShaderConstantBuffer(m_activePixelConstantBufferSlot, m_activePixelConstantBuffer);
        }
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::PushConstantB( IConstantBuffer* _constantBuffer, uint32 _offset, bool* _constant, uint32 _size )
    {
        D3D9ConstantBuffer* d3d9ConstantBuffer = static_cast<D3D9ConstantBuffer*>(_constantBuffer);
        if( _offset + _size > d3d9ConstantBuffer->size )
        {
            return false;
        }
        
        if( d3d9ConstantBuffer->boolBuffer.empty() != false )
        {
            d3d9ConstantBuffer->boolBuffer.resize(d3d9ConstantBuffer->size);
        }        
        
        for( uint32 i = 0; i < _size / sizeof(bool); ++i )
        {
            *(reinterpret_cast<BOOL*>(&(d3d9ConstantBuffer->boolBuffer[_offset])) + i) = static_cast<BOOL>(_constant[i]);
        }
        
        if( m_activeVertexConstantBuffer == d3d9ConstantBuffer )
        {
            SetVertexShaderConstantBuffer(m_activeVertexConstantBufferSlot, m_activeVertexConstantBuffer);
        }
        if( m_activePixelConstantBuffer == d3d9ConstantBuffer )
        {
            SetPixelShaderConstantBuffer(m_activePixelConstantBufferSlot, m_activePixelConstantBuffer);
        }
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ReleaseConstantBuffer( IConstantBuffer* _constantBuffer )
    {
        delete static_cast<D3D9ConstantBuffer*>(_constantBuffer);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IDirect3DVertexDeclaration9* D3D9RenderSystem::createVertexDeclaration_( VertexElement* _elements, uint32 _elementsNum )
    {
        NANE_ASSERT( m_pDevice != NULL, "Device Context must be initialized" );
        D3DVERTEXELEMENT9 d3d9VertexDecl[MAX_VERTEX_ELEMENTS];
        for( uint32 i = 0; i < _elementsNum; ++i )
        {
            d3d9VertexDecl[i].Stream = _elements[i].stream;
            d3d9VertexDecl[i].Offset = _elements[i].offset;
            d3d9VertexDecl[i].Type = s_toD3DVertDeclType[_elements[i].type];
            d3d9VertexDecl[i].Method = D3DDECLMETHOD_DEFAULT;
            d3d9VertexDecl[i].Usage = s_toD3DVertDeclUsage[_elements[i].semantic];
            d3d9VertexDecl[i].UsageIndex = _elements[i].usageIndex;
        };
        d3d9VertexDecl[_elementsNum].Stream = 0xff;
        d3d9VertexDecl[_elementsNum].Offset = 0;
        d3d9VertexDecl[_elementsNum].Type = D3DDECLTYPE_UNUSED;
        d3d9VertexDecl[_elementsNum].Method = 0;
        d3d9VertexDecl[_elementsNum].Usage = 0;
        d3d9VertexDecl[_elementsNum].UsageIndex = 0;

        IDirect3DVertexDeclaration9* pVertexDecl = NULL;
        HRESULT hr = m_pDevice->CreateVertexDeclaration(d3d9VertexDecl, &pVertexDecl);
        SUCCEEDED(hr);
        return pVertexDecl;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IVertexShader* D3D9RenderSystem::CreateVertexShader( const byte* _buffer, uint32 _size )
    {
        IDirect3DVertexShader9* vertexShader = NULL;
        HRESULT hr = m_pDevice->CreateVertexShader(reinterpret_cast<const DWORD*>(_buffer), &vertexShader);
        SUCCEEDED(hr);
        return reinterpret_cast<IVertexShader*>(vertexShader);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::SetVertexShader( IVertexShader* _vertexShader )
    {
        IDirect3DVertexShader9* vertexShader = reinterpret_cast<IDirect3DVertexShader9*>(_vertexShader);
        HRESULT hr = m_pDevice->SetVertexShader(vertexShader);
        return hr == D3D_OK;        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::SetVertexShaderConstantBuffer( uint32 _slot, IConstantBuffer* _constantBuffer )
    {
        D3D9ConstantBuffer* d3d9ConstantBuffer = static_cast<D3D9ConstantBuffer*>(_constantBuffer);
        m_activeVertexConstantBufferSlot = _slot;
        m_activeVertexConstantBuffer = d3d9ConstantBuffer;
        if( d3d9ConstantBuffer == NULL )
        {
            return true;
        }
        
        HRESULT hr1 = D3D_OK;
        HRESULT hr2 = D3D_OK;
        HRESULT hr3 = D3D_OK;
        if( d3d9ConstantBuffer->floatBuffer.empty() == false )
        {
            hr1 = m_pDevice->SetVertexShaderConstantF(static_cast<UINT>(_slot), reinterpret_cast<const float*>(&(d3d9ConstantBuffer->floatBuffer[0])), d3d9ConstantBuffer->size / 4);
        }
        if( d3d9ConstantBuffer->intBuffer.empty() == false )
        {
            hr2 = m_pDevice->SetVertexShaderConstantI(static_cast<UINT>(_slot), reinterpret_cast<const int*>(&(d3d9ConstantBuffer->intBuffer[0])), d3d9ConstantBuffer->size / 4);
        }
        if( d3d9ConstantBuffer->boolBuffer.empty() == false )
        {
            hr3 = m_pDevice->SetVertexShaderConstantB(static_cast<UINT>(_slot), reinterpret_cast<const BOOL*>(&(d3d9ConstantBuffer->boolBuffer[0])), d3d9ConstantBuffer->size / 4);
        }
        
        return SUCCEEDED(hr1) && SUCCEEDED(hr2) && SUCCEEDED(hr3);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ReleaseVertexShader( IVertexShader* _vertexShader )
    {
        IDirect3DVertexShader9* vertexShader = reinterpret_cast<IDirect3DVertexShader9*>(_vertexShader);
        vertexShader->Release();        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    IPixelShader* D3D9RenderSystem::CreatePixelShader( const byte* _buffer, uint32 _size )
    {
        IDirect3DPixelShader9* pixelShader = NULL;
        HRESULT hr = m_pDevice->CreatePixelShader(reinterpret_cast<const DWORD*>(_buffer), &pixelShader);
        SUCCEEDED(hr);
        return reinterpret_cast<IPixelShader*>(pixelShader);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::SetPixelShader( IPixelShader* _pixelShader )
    {
        IDirect3DPixelShader9* pixelShader = reinterpret_cast<IDirect3DPixelShader9*>(_pixelShader);
        HRESULT hr = m_pDevice->SetPixelShader(pixelShader);
        return hr == D3D_OK;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool D3D9RenderSystem::SetPixelShaderConstantBuffer( uint32 _slot, IConstantBuffer* _constantBuffer )
    {
        D3D9ConstantBuffer* d3d9ConstantBuffer = static_cast<D3D9ConstantBuffer*>(_constantBuffer);
        m_activePixelConstantBufferSlot = _slot;
        m_activePixelConstantBuffer = d3d9ConstantBuffer;
        if( d3d9ConstantBuffer == NULL )
        {
            return true;
        }
        
        HRESULT hr1 = D3D_OK;
        HRESULT hr2 = D3D_OK;
        HRESULT hr3 = D3D_OK;
        if( d3d9ConstantBuffer->floatBuffer.empty() == false )
        {
            hr1 = m_pDevice->SetVertexShaderConstantF(static_cast<UINT>(_slot), reinterpret_cast<const float*>(&(d3d9ConstantBuffer->floatBuffer[0])), d3d9ConstantBuffer->size / 4);
        }
        if( d3d9ConstantBuffer->intBuffer.empty() == false )
        {
            hr2 = m_pDevice->SetVertexShaderConstantI(static_cast<UINT>(_slot), reinterpret_cast<const int*>(&(d3d9ConstantBuffer->intBuffer[0])), d3d9ConstantBuffer->size / 4);
        }
        if( d3d9ConstantBuffer->boolBuffer.empty() == false )
        {
            hr3 = m_pDevice->SetVertexShaderConstantB(static_cast<UINT>(_slot), reinterpret_cast<const BOOL*>(&(d3d9ConstantBuffer->boolBuffer[0])), d3d9ConstantBuffer->size / 4);
        }
        return SUCCEEDED(hr1) && SUCCEEDED(hr2) && SUCCEEDED(hr3);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void D3D9RenderSystem::ReleasePixelShader( IPixelShader* _pixelShader )
    {
        IDirect3DPixelShader9* pixelShader = reinterpret_cast<IDirect3DPixelShader9*>(_pixelShader);
        pixelShader->Release();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
